// Created by plusminus on 20:50:06 - 03.10.2008
package com.talkweb.osmharmony.views.overlay;

import com.talkweb.osmharmony.ResourceTable;
import com.talkweb.osmharmony.util.DisplayUtils;
import com.talkweb.osmharmony.util.ResourceHelper;
import com.talkweb.osmharmony.views.MapView;
import com.talkweb.osmharmony.views.Projection;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.element.Element;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;

import java.util.List;

/**
 * @param <Item>
 * @deprecated see {@link Marker}
 * it is generally recommended to use the  {@link Marker} class instead of this.
 * While it does work and is usually maintained, the Marker class as a lot more capabilities
 */
@Deprecated
public class ItemizedOverlayWithFocus<Item extends OverlayItem> extends ItemizedIconOverlay<Item> {

    // ===========================================================
    // Constants
    // ===========================================================

    private final int DEFAULTMARKER_BACKGROUNDCOLOR = Color.rgb(101, 185, 74);


    // ===========================================================
    // Fields
    // ===========================================================

    private int DESCRIPTION_BOX_PADDING = 3;
    private int DESCRIPTION_BOX_CORNERWIDTH = 3;

    /**
     * Additional to <code>DESCRIPTION_LINE_HEIGHT</code>.
     */
    private int DESCRIPTION_TITLE_EXTRA_LINE_HEIGHT = 2;

    private int FONT_SIZE_DP = 14;
    private int DESCRIPTION_MAXWIDTH = 600;
    private int DESCRIPTION_LINE_HEIGHT = 30;

    protected int mMarkerFocusedBackgroundColor;
    protected Paint mMarkerBackgroundPaint, mDescriptionPaint, mTitlePaint;


    protected Element mMarkerFocusedBase;
    protected int mFocusedItemIndex;
    protected boolean mFocusItemsOnTap;
    private int fontSizePixels;
    private final Point mFocusedScreenCoords = new Point();

    private Context mContext;

    private String UNKNOWN;

    // ===========================================================
    // Constructors
    // ===========================================================

    public ItemizedOverlayWithFocus(final Context pContext, final List<Item> aList,
                                    final OnItemGestureListener<Item> aOnItemTapListener) {
        this(aList, aOnItemTapListener, pContext);
    }

    public ItemizedOverlayWithFocus(final List<Item> aList,
                                    final OnItemGestureListener<Item> aOnItemTapListener, Context pContext) {
        this(aList,
                ResourceHelper.getElement(pContext, ResourceTable.Media_marker_default)
                , null, NOT_SET,
                aOnItemTapListener, pContext);
    }

    public ItemizedOverlayWithFocus(final List<Item> aList, final Element pMarker,
                                    final Element pMarkerFocused, final int pFocusedBackgroundColor,
                                    final OnItemGestureListener<Item> aOnItemTapListener, Context pContext) {

        super(aList, pMarker, aOnItemTapListener, pContext);
        mContext = pContext;
        if (pMarkerFocused == null) {
            this.mMarkerFocusedBase = boundToHotspot(
                    ResourceHelper.getElement(pContext, ResourceTable.Media_marker_default_focused_base)
                    , OverlayItem.HotspotPlace.BOTTOM_CENTER);
        } else
            this.mMarkerFocusedBase = pMarkerFocused;

        this.mMarkerFocusedBackgroundColor = (pFocusedBackgroundColor != NOT_SET) ? pFocusedBackgroundColor
                : DEFAULTMARKER_BACKGROUNDCOLOR;

        calculateDrawSettings();

        this.unSetFocusedItem();
    }

    private void calculateDrawSettings() {
        //calculate font size based on DP
//        fontSizePixels = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
//                FONT_SIZE_DP, mContext.getResources().getDisplayMetrics());
        fontSizePixels = AttrHelper.vp2px(FONT_SIZE_DP, mContext);
        DESCRIPTION_LINE_HEIGHT = fontSizePixels + 5;

        //calculate max width based on screen width.
        DESCRIPTION_MAXWIDTH = (int) (DisplayUtils.getDisplayWidthInPx(mContext) * 0.8);
        UNKNOWN = ResourceHelper.getString(mContext, ResourceTable.String_unknown);

        this.mMarkerBackgroundPaint = new Paint(); // Color is set in onDraw(...)

        this.mDescriptionPaint = new Paint();
        this.mDescriptionPaint.setAntiAlias(true);
        this.mDescriptionPaint.setTextSize(fontSizePixels);
        this.mTitlePaint = new Paint();
        this.mTitlePaint.setTextSize(fontSizePixels);
        this.mTitlePaint.setFakeBoldText(true);
        this.mTitlePaint.setAntiAlias(true);
    }

    // ===========================================================
    // Getter & Setter
    // ===========================================================


    /**
     * default is 3 pixels
     *
     * @param value
     */
    public void setDescriptionBoxPadding(int value) {
        DESCRIPTION_BOX_PADDING = value;
    }

    /**
     * default 3
     *
     * @param value
     */
    public void setDescriptionBoxCornerWidth(int value) {
        DESCRIPTION_BOX_CORNERWIDTH = value;
    }

    /**
     * default is 2
     *
     * @param value
     */
    public void setDescriptionTitleExtraLineHeight(int value) {
        DESCRIPTION_TITLE_EXTRA_LINE_HEIGHT = value;
    }

    /**
     * default is a green like color
     *
     * @param value
     */
    public void setMarkerBackgroundColor(int value) {
        mMarkerFocusedBackgroundColor = value;
    }

    public void setMarkerTitleForegroundColor(int value) {
        mTitlePaint.setColor(new Color(value));
    }

    public void setMarkerDescriptionForegroundColor(int value) {
        mDescriptionPaint.setColor(new Color(value));
    }

    /**
     * default is 14
     *
     * @param value
     */
    public void setFontSize(int value) {
        FONT_SIZE_DP = value;
        calculateDrawSettings();
    }

    /**
     * in pixels, default is 600
     *
     * @param value
     */
    public void setDescriptionMaxWidth(int value) {
        DESCRIPTION_MAXWIDTH = value;
        calculateDrawSettings();
    }

    /**
     * default is 30
     *
     * @param value
     */
    public void setDescriptionLineHeight(int value) {
        DESCRIPTION_LINE_HEIGHT = value;
        calculateDrawSettings();
    }

    public Item getFocusedItem() {
        if (this.mFocusedItemIndex == NOT_SET) {
            return null;
        }
        return this.mItemList.get(this.mFocusedItemIndex);
    }

    public void setFocusedItem(final int pIndex) {
        this.mFocusedItemIndex = pIndex;
    }

    public void unSetFocusedItem() {
        this.mFocusedItemIndex = NOT_SET;
    }

    public void setFocusedItem(final Item pItem) {
        final int indexFound = super.mItemList.indexOf(pItem);
        if (indexFound < 0) {
            throw new IllegalArgumentException();
        }

        this.setFocusedItem(indexFound);
    }

    public void setFocusItemsOnTap(final boolean doit) {
        this.mFocusItemsOnTap = doit;
    }

    // ===========================================================
    // Methods from SuperClass/Interfaces
    // ===========================================================

    @Override
    protected boolean onSingleTapUpHelper(final int index, final Item item, final MapView mapView) {
        if (this.mFocusItemsOnTap) {
            this.mFocusedItemIndex = index;
            mapView.invalidate();
        }
        return this.mOnItemGestureListener.onItemSingleTapUp(index, item);
    }

    private final Rect mRect = new Rect();

    @Override
    public void draw(final Canvas c, final Projection pProjection) {

        super.draw(c, pProjection);

        if (this.mFocusedItemIndex == NOT_SET) {
            return;
        }

        // this happens during shutdown
        if (super.mItemList == null)
            return;
        // get focused item's preferred marker & hotspot
        final Item focusedItem = super.mItemList.get(this.mFocusedItemIndex);
        Element markerFocusedBase = focusedItem.getMarker(OverlayItem.ITEM_STATE_FOCUSED_MASK);
        if (markerFocusedBase == null) {
            markerFocusedBase = this.mMarkerFocusedBase;
        }

        /* Calculate and set the bounds of the marker. */
        pProjection.toPixels(focusedItem.getPoint(), mFocusedScreenCoords);

        markerFocusedBase.setBounds(mRect);
        mRect.offset(mFocusedScreenCoords.getPointXToInt(), mFocusedScreenCoords.getPointYToInt());

        /* Strings of the OverlayItem, we need. */
        final String itemTitle = (focusedItem.getTitle() == null) ? UNKNOWN : focusedItem
                .getTitle();
        final String itemDescription = (focusedItem.getSnippet() == null) ? UNKNOWN : focusedItem
                .getSnippet();

        /*
         * Store the width needed for each char in the description to a float array. This is pretty
         * efficient.
         */
        final float[] widths = new float[itemDescription.length()];
        this.mDescriptionPaint.getTextBounds(itemDescription);

        final StringBuilder sb = new StringBuilder();
        int maxWidth = 0;
        int curLineWidth = 0;
        int lastStop = 0;
        int i;
        int lastwhitespace = 0;
        /*
         * Loop through the charwidth array and harshly insert a linebreak, when the width gets
         * bigger than DESCRIPTION_MAXWIDTH.
         */
        for (i = 0; i < widths.length; i++) {
            if (!Character.isLetter(itemDescription.charAt(i))) {
                lastwhitespace = i;
            }

            final float charwidth = widths[i];

            if (itemDescription.charAt(i) == '\n') {
                sb.append(itemDescription.subSequence(lastStop, i + 1));
                lastStop = i + 1;
                maxWidth = Math.max(maxWidth, curLineWidth);
                curLineWidth = 0;
                lastwhitespace = lastStop;
                continue;
            } else if (curLineWidth + charwidth > DESCRIPTION_MAXWIDTH) {
                boolean noSpace = lastStop == lastwhitespace;
                if (!noSpace) {
                    i = lastwhitespace;
                }

                sb.append(itemDescription.subSequence(lastStop, i));
                sb.append('\n');

                lastStop = i;
                maxWidth = Math.max(maxWidth, curLineWidth);
                curLineWidth = 0;
                lastwhitespace = lastStop;
                if (noSpace) {
                    i--;
                    continue;
                }
            }

            curLineWidth += charwidth;
        }
        /* Add the last line to the rest to the buffer. */
        if (i != lastStop) {
            final String rest = itemDescription.substring(lastStop, i);
            maxWidth = Math.max(maxWidth, (int) this.mDescriptionPaint.measureText(rest));
            sb.append(rest);
        }
        final String[] lines = sb.toString().split("\n");

        /*
         * The title also needs to be taken into consideration for the width calculation.
         */
        final int titleWidth = (int) this.mDescriptionPaint.measureText(itemTitle);

        maxWidth = Math.max(maxWidth, titleWidth);
        final int descWidth = Math.min(maxWidth, DESCRIPTION_MAXWIDTH);

        /* Calculate the bounds of the Description box that needs to be drawn. */
        final int descBoxLeft = mRect.left - descWidth / 2 - DESCRIPTION_BOX_PADDING
                + mRect.getWidth() / 2;
        final int descBoxRight = descBoxLeft + descWidth + 2 * DESCRIPTION_BOX_PADDING;
        final int descBoxBottom = mRect.top;
        final int descBoxTop = descBoxBottom - DESCRIPTION_TITLE_EXTRA_LINE_HEIGHT
                - (lines.length + 1) * DESCRIPTION_LINE_HEIGHT /* +1 because of the title. */
                - 2 * DESCRIPTION_BOX_PADDING;

        if (pProjection.getOrientation() != 0) {
            c.save();
            c.rotate(-pProjection.getOrientation(), mFocusedScreenCoords.getPointX(), mFocusedScreenCoords.getPointY());
        }

        /* Twice draw a RoundRect, once in black with 1px as a small border. */
        this.mMarkerBackgroundPaint.setColor(Color.BLACK);
        c.drawRoundRect(new RectFloat(descBoxLeft - 1, descBoxTop - 1, descBoxRight + 1,
                        descBoxBottom + 1), DESCRIPTION_BOX_CORNERWIDTH, DESCRIPTION_BOX_CORNERWIDTH,
                this.mDescriptionPaint);
        this.mMarkerBackgroundPaint.setColor(new Color(this.mMarkerFocusedBackgroundColor));
        c.drawRoundRect(new RectFloat(descBoxLeft, descBoxTop, descBoxRight, descBoxBottom),
                DESCRIPTION_BOX_CORNERWIDTH, DESCRIPTION_BOX_CORNERWIDTH,
                this.mMarkerBackgroundPaint);

        final int descLeft = descBoxLeft + DESCRIPTION_BOX_PADDING;
        int descTextLineBottom = descBoxBottom - DESCRIPTION_BOX_PADDING;

        /* Draw all the lines of the description. */
        for (int j = lines.length - 1; j >= 0; j--) {
            c.drawText(this.mDescriptionPaint,lines[j].trim(), descLeft, descTextLineBottom);
            descTextLineBottom -= DESCRIPTION_LINE_HEIGHT;
        }
        /* Draw the title. */
        c.drawText(this.mTitlePaint,itemTitle, descLeft,
                descTextLineBottom - DESCRIPTION_TITLE_EXTRA_LINE_HEIGHT);
        c.drawLine(descBoxLeft, descTextLineBottom, descBoxRight, descTextLineBottom,
                mDescriptionPaint);

        /*
         * Finally draw the marker base. This is done in the end to make it look better.
         */
        markerFocusedBase.setBounds(mRect);
        markerFocusedBase.drawToCanvas(c);
        mRect.offset(-mFocusedScreenCoords.getPointXToInt(), -mFocusedScreenCoords.getPointYToInt());
        markerFocusedBase.setBounds(mRect);

        if (pProjection.getOrientation() != 0) {
            c.restore();
        }
    }

    @Override
    public void onDetach(MapView mapView) {
        super.onDetach(mapView);
        this.mContext = null;
    }

    // ===========================================================
    // Methods
    // ===========================================================

    // ===========================================================
    // Inner and Anonymous Classes
    // ===========================================================
}
